﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using DevExpress.XtraTab;
using DevExpress.XtraEditors;
using System.Threading.Tasks;
using System.Text.RegularExpressions;

namespace BuildHelper.Work.Tools.ScriptCompare
{
    public partial class CompareForm : XtraForm
    {
        NamedList n1 = new NamedList("Duplikaty w pliku 1");
        NamedList n2 = new NamedList("Duplikaty w pliku 2");
        NamedList b1 = new NamedList("Braki w pliku 1");
        NamedList b2 = new NamedList("Braki w pliku 2");
        NamedList d = new NamedList("Różnice");

        bool bb1 = false;
        bool bb2 = false;
        bool bd1 = false;
        bool bd2 = false;
        bool bd = false;
        Dictionary<int, string> columns;
        List<string> key;

        public CompareForm()
        {
            InitializeComponent();
        }

        private void templateControl1_ReadingTemplate(object sender, Template.MergingDataSetEventArgs e)
        {
            if (e.TemplateDataSet.Tables.Contains("SettingsMain"))
            {
                DataRow r = e.TemplateDataSet.Tables["SettingsMain"].SelectFirstRow();
                if (r != null)
                {
                    teMaska.Text = r["Maska"].AsString();
                    ceSingleline.Checked = r["Singleline"].AsBool();
                    ceMultiline.Checked = r["Multiline"].AsBool();
                    ceIgnoreCase.Checked = r["IgnoreCase"].AsBool();

                }
            }
        }

        private void templateControl1_SavingTemplate(object sender, Template.MergingDataSetEventArgs e)
        {
            DataRow r = e.TemplateDataSet.Tables["SettingsMain"].SelectFirstRow();
            if (r == null)
            {
                r = e.TemplateDataSet.Tables["SettingsMain"].NewRow();
                e.TemplateDataSet.Tables["SettingsMain"].Rows.Add(r);
            }
            r["Maska"] = teMaska.EditValue.AsString();
            r["Singleline"] = ceSingleline.Checked;
            r["Multiline"] = ceMultiline.Checked;
            r["IgnoreCase"] = ceIgnoreCase.Checked;
        }

        private void Clear()
        {
             var pages = tabWyniki.TabPages.ToArray();
             foreach (var p in pages)
             {
                 p.Dispose();
             }
             tabWyniki.TabPages.Clear();
        }
        bool inProgress = false;

        private void btnCompare_Click(object sender, EventArgs e)
        {
            if (inProgress)
            {
                this.ShowDxInfoDialog("Operacja w toku.");
                return;
            }
            Clear();
            cancel = false;
            inProgress = true;
           
            Task t = Task.Factory.StartNew(Compare);
        }
        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            cancel = true;
            base.OnFormClosing(e);
        }
        private void Compare()
        {
            try
            {
                List<NamedList> list = new List<NamedList>();
                list.Add(n1);
                list.Add(n2);
                list.Add(b1);
                list.Add(b2);
                list.Add(d);
                foreach (var i in list)
                    i.Clear();
                
                string mask = teMaska.EditValue.AsString();
                string f1 = edFile1.EditValue.AsString().ReadFile();
                string f2 = edFile2.EditValue.AsString().ReadFile();

                bb1 = ceBraki1.Checked;
                bb2 = ceBraki2.Checked;
                bd1 = ceDuplikaty1.Checked;
                bd2 = ceDuplikaty2.Checked;
                bd = ceDiff.Checked;
                
                SetLabelText(lblBraki1, string.Empty);
                SetLabelText(lblBraki2, string.Empty);
                SetLabelText(lblDuplikaty1, string.Empty);
                SetLabelText(lblDuplikaty2, string.Empty);
                SetLabelText(lblDiff, string.Empty);

                key = new List<string>();
                columns = new Dictionary<int, string>();

                foreach (DataRow r in this.scriptCompareDataSet.Settings.Select())
                {
                    columns.Add(r["NumerGrupy"].AsInt(), r["Nazwa"].AsString());
                    if (r["Klucz"].AsBool())
                        key.Add(r["Nazwa"].AsString());
                }
                RegexOptions op = RegexOptions.None;
                if (ceMultiline.Checked)
                    op |= RegexOptions.Multiline;
                if (ceIgnoreCase.Checked)
                    op |= RegexOptions.IgnoreCase;
                if (ceSingleline.Checked)
                    op |= RegexOptions.Singleline;

                var dic1 = f1.GetMatches(mask, columns.Keys.ToArray(), op);
                var dic2 = f2.GetMatches(mask, columns.Keys.ToArray(), op);

                List<ScriptData> items1 = ReadItems(dic1);
                List<ScriptData> items2 = ReadItems(dic2);

                ProcessCompare(items1, items2);

                foreach (var i in list)
                    AddPage(tabWyniki, i);
            }
            catch (Exception ex)
            {
                ex.Log();
            }
            finally
            {
                inProgress = false;
            }
        }

        private List<ScriptData> ReadItems(List<Dictionary<int,string>> dic)
        {
            List<ScriptData> list = new List<ScriptData>();
            foreach (var item in dic)
            {
                ScriptData s = new ScriptData();
                foreach (var x in columns)
                {
                    s[x.Value] = item[x.Key];
                }
                list.Add(s);
            }
            return list;
        }

        private void SetLabelText(Control c, string text)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new Action<Control, string>(SetLabelText), c, text);
                return;
            }
            c.Text = text;
        }
        private void AddPage(DevExpress.XtraTab.XtraTabControl tab,NamedList list)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new Action<XtraTabControl, NamedList>(AddPage), tab, list);
                return;
            }
            if (list.Count > 0)
            {
                XtraTabPage p = null;
                MemoEdit me = null;
                p = new XtraTabPage();
                p.Text = list.Name;
                me = new MemoEdit();
                me.Dock = DockStyle.Fill;
                me.Properties.ReadOnly = true;
                p.Controls.Add(me);
                tab.TabPages.Add(p);
                me.Lines = list.ToArray();
                list.Clear();
            }
        }

        private void ProcessCompare(List<ScriptData> items1, List<ScriptData> items2)
        {
            int idx = 0;
            ScriptData item2;
            List<ScriptData> usedList2 = new List<ScriptData>();
            
            List<string> added = new List<string>();
            List<string> duplicated = new List<string>();
            string keyStr;
            bool dup = false;
                

            if (bb2 || bd1 || bd)
            {
                foreach (var i1 in items1)
                {
                    idx++;
                    keyStr = i1.ToString(key);
                    dup = false;
                    if (bd1)
                    {
                        SetLabelText(lblDuplikaty1, idx + "/" + items1.Count);
                        dup = IsDuplicated(added, duplicated, keyStr, n1);
                    }
                    if (bb2 || bd)
                    {
                        item2 = GetItemByKey(items2, i1, key);
                        if (bb2)
                            SetLabelText(lblBraki2, idx + "/" + items1.Count);
                        if (bd)
                            SetLabelText(lblDiff, idx + "/" + items1.Count);
                        if (item2 != null)
                            usedList2.Add(item2);
                        if (!dup)
                        {
                            if (bb2 && item2 == null)
                                b2.Add("Missing: " + keyStr);
                            if (bd && item2 != null && i1.IsDifferent(item2, key))
                                d.Add("Difference: " + i1.DiffString(item2, key));
                        }
                    }
                }
            }
            if (bd2 || bb1)
            {
                idx = 0;
                added.Clear();
                duplicated.Clear();
                foreach (var i2 in items2)
                {
                    idx++;
                    keyStr = i2.ToString(key);
                    dup = false;
                    if (bd2)
                    {
                        SetLabelText(lblDuplikaty2, idx + "/" + items2.Count);
                        dup = IsDuplicated(added, duplicated, keyStr, n2);
                    }
                    if (bb1)
                    {
                        SetLabelText(lblBraki1, idx + "/" + items2.Count);
                        if (!dup)
                        {
                            if (!usedList2.Contains(i2))
                            {
                                item2 = GetItemByKey(items1, i2, key);
                                if (item2 == null)
                                    b1.Add("Missing: " + keyStr);
                            }
                        }
                    }
                }
            }
        }

        private bool IsDuplicated(List<string> added, List<string> duplicated, string keyStr, NamedList list)
        {
            bool result = false;
            if (added.Contains(keyStr))
            {
                result = true;
                if (!duplicated.Contains(keyStr))
                {
                    duplicated.Add(keyStr);
                    list.Add("Duplicate: " + keyStr);
                }
            }
            else
            {
                added.Add(keyStr);
            }
            return result;
        }
        
        private bool ValidateAll()
        {
            return true;
        }

        private ScriptData GetItemByKey(List<ScriptData> items, ScriptData item, List<string> key)
        {
            foreach (var i in items)
            {
                if (i.Match(item, key))
                    return i;
            }
            return null;
        }
        private bool cancel = false;
        private void btnCancel_Click(object sender, EventArgs e)
        {
            cancel = true;
        }
    }

    public class ScriptData
    {
        private Dictionary<string, string> Fields = new Dictionary<string, string>();
        public string this[string field]
        {
            get
            {
                if (ContainsField(field))
                    return Fields[field];
                return null;
            }
            set
            {
                Fields[field] = value;
            }
        }

        public bool ContainsField(string field)
        {
            return field.IsNotNull() && Fields.ContainsKey(field);
        }

        public bool Match(ScriptData item, List<string> key)
        {
            foreach (string s in key)
            {
                if (item[s] != this[s])
                {
                    return false;
                }
            }
            return true;
        }
        public string ToString(List<string> key)
        {
            string s = string.Empty;
            foreach (string k in key)
            {
                s = s.AppendString(";");
                s += k + " = " + this[k];
            }
            return s;
        }
        public string DiffString(ScriptData item, List<string> key)
        {
            string s = string.Empty;
            foreach (string k in key)
            {
                s = s.AppendString(";");
                s += k + " = " + this[k];
            }
            s = "[" + s + " ] Difference:";
            string dif = string.Empty;
            foreach (var kvp in this.Fields)
            {
                if (!key.Contains(kvp.Key))
                {
                    if (kvp.Value != item[kvp.Key])
                    {
                        dif = dif.AppendString("; ");
                        dif += "[" + kvp.Key + "] = " + kvp.Value + " / " + item[kvp.Key];
                    }
                }
            }
            s += dif;
            return s;
        }
        public bool IsDifferent(ScriptData item, List<string> key)
        {
            foreach (var kvp in this.Fields)
            {
                if (!key.Contains(kvp.Key))
                {
                    if (kvp.Value != item[kvp.Key])
                    {
                        return true;
                    }
                }
            }
            return false;
        }
    }
}
